# -*- coding: utf-8 -*-

"""
Module implementing MainWindow_slots.
"""
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import pyqtSlot, QRegExp, Qt, QRect
from PyQt5.QtGui import QRegExpValidator, QStandardItem, QStandardItemModel, QBrush, QColor
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox, QFileDialog, QDialog, QDesktopWidget
from Ui_risk_test_mainwindow import Ui_MainWindow
from Ui_Oracle_Set_Dialog import Ui_Dialog
from Ui_Qss import MainWindow_Qss
import os
import re
import stat
import paramiko
import sys
from subprocess import Popen, PIPE
import time
import configparser
import csv
import traceback
import logging

# logging.basicConfig(filename="Debug.log", level=logging.DEBUG, format=" [%(asctime)s] - [%(levelname)s] - [%(message)s]")
logging.basicConfig(level=logging.DEBUG, format=" [%(asctime)s] - [%(levelname)s] - [%(message)s]")
# logging.disable(logging.DEBUG)
#TODO:打包前记得更改日志配置

class MainWindow_slots(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(MainWindow_slots, self).__init__(parent)
        self.setupUi(self)
        self.tabWidget_2.setGeometry(QRect(0, 0, 600, 500)) # 设置tabwidget显示大小，在qtdesiger不好设置
        # 窗体居中
        self.set_window_center()
        # 控件默认设置
        self.default_option()
        # 设置控件QSS显示风格
        self.set_uistyle(p_customer_theme_name=get_custom_ini())
        # 读取Oralce配置
        self.get_config()

    """ *****************************窗体初始化_开始********************************* """
    # 窗体居中
    def set_window_center(self):
        screen = QDesktopWidget().screenGeometry() # 该语句用来计算出显示器的分辨率（screen.width, screen.height）。
        size = self.geometry()  # 该语句用来获取 QWidget 窗口的大小（size.width,size.height）。
        # 该语句将窗口移动到屏幕的中间位置。这里move语句是将窗口的左上角移动到((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2)这个位置
        self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2)

    # 设置控件QSS显示风格
    def set_uistyle(self, p_customer_theme_name):
        self.setStyleSheet("") # 先清除样式
        if p_customer_theme_name == 'Fusion':
            QApplication.setStyle('Fusion')
        elif p_customer_theme_name == 'WindowsXP':
            QApplication.setStyle('WindowsXP')
        elif p_customer_theme_name == 'Windows':
            QApplication.setStyle('Windows')
        elif p_customer_theme_name == 'WindowsVista':
            QApplication.setStyle('WindowsVista')
        elif p_customer_theme_name == 'Dark':
            self.setStyleSheet(MainWindow_Qss.Ui_Qss)

    # 控件默认设置
    def default_option(self):
        # radioButton.setChecked(True)的选中方法
        self.radioButton_6.setChecked(True) # 产品级
        self.radioButton.setChecked(True) # 上限
        self.radioButton_9.setChecked(True)  # 买
        self.radioButton_3.setChecked(True) # 计算余券
        self.radioButton_7.setChecked(True) # entrust_peop 买卖
        self.radioButton_11.setChecked(True)  # entrust_type 普通委托
        self.radioButton_13.setChecked(True)  # 默认 计算行权代交收
        self.radioButton_15.setChecked(True) # exchange_type 默认选择上海
        regExp = QRegExp("(\d*)")  # 正则表达式 只允许输入正整数
        self.lineEdit_5.setValidator(QRegExpValidator(regExp))  # 限制输入类型
        self.lineEdit.setValidator(QRegExpValidator(regExp))  # 限制输入类型
        self.lineEdit_2.setValidator(QRegExpValidator(regExp))  # 限制输入类型
        regExp = QRegExp("^[1-9]*\d*[.]\d*$")  # 正则表达式 只允许输入正浮点数
        self.lineEdit_3.setValidator(QRegExpValidator(regExp))  # 限制输入类型

    # 获取配置
    def get_config(self):
        # 读取config\config.ini文件
        try:
            get_config_ini()
        except Exception as e:
            log = "【ERROR:】获取配置文件config.ini失败，请检查配置文件...\n 详细信息:" + traceback.format_exc()
            logging.debug(log)
            self.textBrowser_2.append(log)

    """ *****************************窗体初始化_结束********************************* """

    """ *****************************菜单槽函数_开始********************************* """
    # 点击 保存计算结果
    @pyqtSlot()
    def on_action_triggered(self):
        # 检查log目录是否存在，如果不存在则创建log目录
        check_log_exist()
        # time.strftime(format[, t])：把一个代表时间的元组或者struct_time（如由time.localtime()和time.gmtime()返回）转化为格式化的时间字符串。
        date = time.strftime("%Y%m%d_%H-%M-%S", time.localtime())
        try:
            # 第三个参数选择保存的默认路径 “.\log” 保存默认名 “计算结果" + date + .txt”,保存的格式类型为*.txt 或 *.*
            save_file_address = QFileDialog.getSaveFileName(self,
                                                            "计算结果另存为",
                                                            ".\\log\\计算结果" + date + ".txt",
                                                            "TXT(*.txt);;ALL(*.*)")
            # 这里判断下save_file_address是否有数据，才向下执行
            # any判断dict,list,tuple是不是为空 为空返回False,非空返回True
            if any(save_file_address):
                save_file_address = save_file_address[0]
                with open(save_file_address, "a+") as f:
                    str = self.textBrowser.toPlainText() # 获取textBrowser中的内容写入文件
                    f.write(str)
                QMessageBox.information(self, '提示', '保存成功', QMessageBox.Ok)
        except:
            log = "【ERROR:】Save File Exception 保存计算结果失败...\n详细信息:" + traceback.format_exc()
            logging.debug(log)
            self.textBrowser_2.append(log)

    # 点击 保存日志
    @pyqtSlot()
    def on_action_2_triggered(self):
        # 检查log目录是否存在，如果不存在则创建log目录
        check_log_exist()
        # time.strftime(format[, t])：把一个代表时间的元组或者struct_time（如由time.localtime()和time.gmtime()返回）转化为格式化的时间字符串。
        date = time.strftime("%Y%m%d_%H-%M-%S", time.localtime())
        try:
            # 第三个参数选择保存的默认路径 “.\log” 保存默认名 “未命名.log”
            save_file_address = QFileDialog.getSaveFileName(self,
                                                            "日志另存为",
                                                            ".\\log\\日志" + date + ".log",
                                                            "LOG(*.log);;ALL(*.*)")
            # 这里判断下save_file_address是否有数据，才向下执行
            # any判断dict,list,tuple是不是为空 为空返回False,非空返回True
            if any(save_file_address):
                save_file_address = save_file_address[0]
                with open(save_file_address, "a+") as f:
                    str = self.textBrowser_2.toPlainText() # 获取textBrowser中的内容写入文件
                    f.write(str)
                QMessageBox.information(self, '提示', '保存成功', QMessageBox.Ok)
        except:
            log = "【ERROR:】Save File Exception 保存日志失败:\n详细信息:" + traceback.format_exc()
            logging.debug(log)
            self.textBrowser_2.append(log)

    # 点击 退出
    @pyqtSlot()
    def on_action_3_triggered(self):
        sys.exit()

    # 点击 数据库连接配置
    @pyqtSlot()
    def on_action_4_triggered(self):
        oralce_set_dialog = Oralce_Set_Dialog_Slots()
        oralce_set_dialog.exec_()

    # 点击 保存结果视图
    @pyqtSlot()
    def on_action_5_triggered(self):
        # 检查log目录是否存在，如果不存在则创建log目录
        check_log_exist()
        # time.strftime(format[, t])：把一个代表时间的元组或者struct_time（如由time.localtime()和time.gmtime()返回）转化为格式化的时间字符串。
        date = time.strftime("%Y%m%d_%H-%M-%S", time.localtime())
        try:
            # 第三个参数选择保存的默认路径 “.\log” 保存默认名 “计算结果" + date + .txt”,保存的格式类型为*.txt 或 *.*
            save_file_address = QFileDialog.getSaveFileName(self, "计算结果另存为", ".\\log\\结果视图" + date + ".csv", "CSV(*.csv);;ALL(*.*)")
            if any(save_file_address):
                save_table_model = self.tableView.model() # 返回 QStandardItemModel Class
                column_count = save_table_model.columnCount()
                row_count = save_table_model.rowCount()
                with open(save_file_address[0], "w", newline="") as csv_write_file:
                    csv_file_writer = csv.writer(csv_write_file)
                    for i in range(row_count):
                        row_list = []
                        for j in range(column_count):
                            table_index = save_table_model.index(i, j)  # 返回 QModelIndex Class
                            item_value = table_index.data(Qt.DisplayRole)  # 等于 data(0)
                            row_list.append(item_value)
                        csv_file_writer.writerow(row_list)
                QMessageBox.information(self, '提示', '保存成功', QMessageBox.Ok)
        except:
            log = "【ERROR:】Save File Exception 保存结果视图失败...\n详细信息:" + traceback.format_exc()
            logging.debug(log)
            self.textBrowser_2.append(log)

    # 点击 主题->经典主题->Fusion
    @pyqtSlot()
    def on_actionFusion_triggered(self):
        self.set_uistyle(p_customer_theme_name='Fusion')
        write_custom_ini(p_customer_theme_name='Fusion')

    # 点击 主题->经典主题->WindowsXP
    @pyqtSlot()
    def on_actionWindowsXP_triggered(self):
        self.set_uistyle(p_customer_theme_name='WindowsXP')
        write_custom_ini(p_customer_theme_name='WindowsXP')

    # 点击 主题->经典主题->Windows
    @pyqtSlot()
    def on_actionWindows_triggered(self):
        self.set_uistyle(p_customer_theme_name='Windows')
        write_custom_ini(p_customer_theme_name='Windows')

    # 点击 主题->经典主题->WindowsVista
    @pyqtSlot()
    def on_actionWindowsVista_triggered(self):
        self.set_uistyle(p_customer_theme_name='WindowsVista')
        write_custom_ini(p_customer_theme_name='WindowsVista')

    # 点击 主题->经典主题->暗黑主题
    @pyqtSlot()
    def on_action_7_triggered(self):
        self.set_uistyle(p_customer_theme_name='Dark')
        write_custom_ini(p_customer_theme_name='Dark')

    @pyqtSlot()
    def on_radioButton_11_clicked(self):
        self.radioButton_7.setCheckable(True)
        self.radioButton_8.setCheckable(True)

    @pyqtSlot()
    def on_radioButton_12_clicked(self):
        self.radioButton_7.setCheckable(True)
        self.radioButton_8.setCheckable(False)

    # 点击 开始计算 按钮s
    @pyqtSlot()
    def on_pushButton_clicked(self):
        v_account_no = self.lineEdit_5.text()
        v_stock_code_c = self.lineEdit.text()
        v_entrust_amount_c = self.lineEdit_2.text()
        log = "******************************" + "计算开始,时间戳：" + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + "******************************"
        self.textBrowser_2.append(log)
        # 如果是事前tab页且代码或数量没有键入
        # if ... elif ... else 如果满足if的条件，则不再向下判断elif
        if v_account_no == '':
            self.message_warning = QMessageBox.warning(self, 'Warning', '请输入账户编号与对应帐户级别', QMessageBox.Ok)
        elif (self.tabWidget.currentIndex() == 0) and (v_stock_code_c == '' or v_entrust_amount_c == ''):
            self.message_warning = QMessageBox.warning(self, 'Warning', '请输入本笔委托代码与数量', QMessageBox.Ok)
        else:
            self.read_file()

    # 点击 明细/视图清空 按钮
    @pyqtSlot()
    def on_pushButton_2_clicked(self):
        self.textBrowser.setText('')
        if str(type(self.tableView.model())) == "<class 'PyQt5.QtGui.QStandardItemModel'>": # 判断当前tableView是否有数据，如果有数据才清楚PyQt5.QtGui.QStandardItemModel
            self.tableView.model().clear() # QStandardItemModel.clear() 清空tableView上model数据

    # 点击 清空日志 按钮
    @pyqtSlot()
    def on_pushButton_3_clicked(self):
        self.textBrowser_2.setText("")

    """ *****************************菜单槽函数_结束********************************* """

    # 读取文件```
    def read_file(self):
        # 删除linux中的文件
        try:
            self.delete_file(p_oracle_host=Global_Variables.G_oracle_host,
                             p_oracle_port=Global_Variables.G_oracle_port,
                             p_oracle_name=Global_Variables.G_oracle_name,
                             p_oracle_password=Global_Variables.G_oracle_password,
                             p_oracle_remote=Global_Variables.G_oracle_remote)
        except:
            log = "【ERROR:】Delete File Exception 删除文件失败:\n详细信息:" + traceback.format_exc()
            logging.debug(log)
            self.textBrowser_2.append(log)
        # 开始计算
        try:
            v_return_no = self.calculate_creat_file()
            if v_return_no == -1:
                return -1 # 返回错误
        except:
            log = "【ERROR:】Creat_File Exception:\n详细信息:" + traceback.format_exc()
            logging.debug(log)
            self.textBrowser_2.append(log)
        # 下载文件
        try:
            self.sftp_download(p_oracle_host=Global_Variables.G_oracle_host,
                               p_oracle_port=Global_Variables.G_oracle_port,
                               p_oracle_name=Global_Variables.G_oracle_name,
                               p_oracle_password=Global_Variables.G_oracle_password,
                               p_local=local,
                               p_oracle_remote=Global_Variables.G_oracle_remote)
        except:
            log = "【ERROR:】Sftp_download Exception: 下载Linux中文件失败\n详细信息:" + traceback.format_exc()
            logging.debug(log)
            self.textBrowser_2.append(log)

        # 获取文件*.txt 并展示在textBrowser
        try:
            self.show_textOnTextBrowser()
        except:
            log = "【ERROR:】Read File Exception:\n详细信息:" + traceback.format_exc()
            logging.debug(log)
            self.textBrowser_2.append(log)
        # 读取csv文件并展示成table_view视图
        try:
            self.read_csv_file()
        except:
            log = "【ERROR:】Read CSV File Exception:\n详细信息:" + traceback.format_exc()
            logging.debug(log)
            self.textBrowser_2.append(log)

    # 调用sqlplus执行存储过程
    def calculate_creat_file(self):
        # 账户
        v_account_no = self.lineEdit_5.text()
        # 证券代码
        v_stock_code_c = self.lineEdit.text()
        v_stock_code_c = "'" + v_stock_code_c + "'" # sqlplus执行时对字符串加上单引号，防止“000001”代码到oracle执行时变为“1”
        # 委托数量
        v_entrust_amount_c = self.lineEdit_2.text()
        # 委托价格
        v_entrust_price_c = self.lineEdit_3.text()
        if self.radioButton_5.isChecked():
            v_account_level = '0' # 交易单元级
        if self.radioButton_6.isChecked():
            v_account_level = '2' # 产品级
        if self.radioButton.isChecked():
            v_up_or_down = '1' # 上限
        if self.radioButton_2.isChecked():
            v_up_or_down = '0' # 下限
        if self.radioButton_3.isChecked():
            v_flag_surplus = '1' # 计算余券
        if self.radioButton_4.isChecked():
            v_flag_surplus = '0' # 不计算余券
        if self.radioButton_9.isChecked():
            v_entrust_bs_c = '1' # 买方向
        if self.radioButton_10.isChecked():
            v_entrust_bs_c = '2' # 卖方向
        if self.radioButton_7.isChecked():
            v_entrust_prop_c = '0' # 买卖
        if self.radioButton_8.isChecked():
            v_entrust_prop_c = '4' # 回购
        if self.radioButton_11.isChecked():
            v_entrust_type_c = '0' # 普通
        if self.radioButton_12.isChecked():
            v_entrust_type_c = '9' # 信用交易
        if self.radioButton_13.isChecked():
            v_flag_optexeassign_c = '1'  # 计算行权代交收
        if self.radioButton_14.isChecked():
            v_flag_optexeassign_c = '0'  # 不计算行权代交收
        if self.radioButton_15.isChecked():
            v_exchange_type_c = '1' # 上海市场
        if self.radioButton_16.isChecked():
            v_exchange_type_c = '2' # 深圳市场
        if self.radioButton_17.isChecked():
            v_exchange_type_c = "'G'"  # 港股通 sqlplus执行时对字符串加上单引号
        if self.radioButton_18.isChecked():
            v_exchange_type_c = "'S'"  # 深港通 sqlplus执行时对字符串加上单引号

        # 使用tabWidget类的currentIndex()方法，获取当前选中的tab页的Index
        if self.tabWidget.currentIndex() == 0:
            risk_type = '0' # 事前风控标签页选中
        elif self.tabWidget.currentIndex() == 1:
            risk_type = '1' # 事中风控标签页选中
        if risk_type == '0': # 事前风控
            try:
                sql = "EXEC PKG_PTRISK_EXEC.PTRISK_BEFOR_EXECUTABLE_SCRIPT(p_account_no => " + v_account_no \
                                                                      + ", p_account_level => " + v_account_level \
                                                                      + ", p_up_or_down => " + v_up_or_down \
                                                                      + ", p_flag_surplus => " + v_flag_surplus \
                                                                      + ", p_flag_optexeassign => " + v_flag_optexeassign_c \
                                                                      + ", p_stock_code_c => " + v_stock_code_c \
                                                                      + ", p_entrust_price_c => " + v_entrust_price_c \
                                                                      + ", p_exchange_type_c => " + v_exchange_type_c \
                                                                      + ", p_entrust_amount_c => " + v_entrust_amount_c \
                                                                      + ", p_entrust_bs_c => " + v_entrust_bs_c \
                                                                      + ", p_entrust_prop_c => " + v_entrust_prop_c \
                                                                      + ", p_entrust_type_c => " + v_entrust_type_c \
                                                                      + ")"
                log = "SQLPlus 事前风控 执行:\n" + sql
                self.textBrowser_2.append(log)
                sqlplus_connect_config = Global_Variables.G_db_user_name + '/' + Global_Variables.G_db_user_password + '@' + Global_Variables.G_oracle_host + '/' + 'orcl'
                proc = Popen(["sqlplus", sqlplus_connect_config], stdout=PIPE, stdin=PIPE, stderr=PIPE)  # 模拟win系统上cmd命令 sqlplus输入
                proc.stdin.write(bytes(sql, encoding='gbk'))
                (out, err) = proc.communicate()
                if proc.returncode != 0:
                    logging.debug('【ERROR:】err:{0}'.format(str(err, encoding='gbk')))
                    log = "【ERROR:】SQLPlus ERR:" + str(err, encoding='gbk')
                    self.textBrowser_2.append(log)
                    return -1  # 返回错误
                else:
                    logging.debug('【SUCCESS:】out:'.format(str(out, encoding='gbk')))  # 用GBK编码转换成字符串
                    log = "【SUCCESS:】SQLPlus OUT:" + str(out, encoding='gbk')
                    self.textBrowser_2.append(log)
                    v_ora_error = re.findall("ERROR:(.*)", str(out, encoding='gbk'), re.S)  # 返回list re.S换行匹配否则之匹配\n之前的字符
                    if len(v_ora_error):
                        QMessageBox.critical(self, 'Error_数据库连接失败', v_ora_error[0], QMessageBox.Ok)
                        return -1 # 返回错误


            except:
                log = "【ERROR:】SQLPlus EXEC Exception:\n详细信息:" + traceback.format_exc()
                logging.debug(log)
                self.textBrowser_2.append(log)

        elif risk_type == '1': # 事中风控
            try:
                sql = "EXEC PKG_PTRISK_EXEC.PTRISK_EXECUTABLE_SCRIPT(p_account_no => " + v_account_no \
                      + ", p_account_level => " + v_account_level \
                      + ")"
                log = "SQLPlus 事中风控 执行:\n" + sql
                self.textBrowser_2.append(log)
                sqlplus_connect_config = Global_Variables.G_db_user_name + '/' + Global_Variables.G_db_user_password + '@' + Global_Variables.G_oracle_host + '/' + 'orcl'
                proc = Popen(["sqlplus", sqlplus_connect_config], stdout=PIPE, stdin=PIPE, stderr=PIPE)  # 模拟win系统上cmd命令 sqlplus输入
                proc.stdin.write(bytes(sql, encoding='gbk'))
                (out, err) = proc.communicate()
                if proc.returncode != 0:
                    logging.debug('【ERROR:】err:'.format(str(err, encoding='gbk')))
                    log = "【ERROR:】SQLPlus ERR:" + str(err, encoding='gbk')
                    self.textBrowser_2.append(log)
                    return -1  # 返回错误
                else:
                    logging.debug('【SUCCESS:】out:{0}'.format(str(out, encoding='gbk')))  # 用GBK编码转换成字符串
                    log = "【SUCCESS:】SQLPlus OUT:" + str(out, encoding='gbk')
                    self.textBrowser_2.append(log)
                    v_ora_error = re.findall("ERROR:(.*)", str(out, encoding='gbk'), re.S) # 返回list re.S换行匹配否则之匹配\n之前的字符
                    if len(v_ora_error):
                        QMessageBox.critical(self, 'Error_数据库连接失败', v_ora_error[0], QMessageBox.Ok)
                        return -1  # 返回错误
            except:
                log = "【ERROR:】SQLPlus EXEC Exception：\n详细信息:" + traceback.format_exc()
                logging.debug(log)
                self.textBrowser_2.append(log)

    # 删除linux中的文件
    def delete_file(self, p_oracle_host, p_oracle_port, p_oracle_name, p_oracle_password, p_oracle_remote):
        ssh = paramiko.SSHClient()  # 创建一个SSHClient的实例化对象
        try:
            # 下面代码的作用是允许连接不在know_hosts文件中的主机。
            ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
            # ssh.connect(p_oracle_host, p_oracle_port, p_oracle_name, p_oracle_password)  # ssh创建连接
            ssh.connect(hostname=p_oracle_host, port=p_oracle_port, username=p_oracle_name, password=p_oracle_password)
            command = 'rm -rf ' + p_oracle_remote.split(".")[0] + "*"
            stdin, stdout, stderr = ssh.exec_command(command)  # 执行linux命令
            # str = stdout.readlines()  # 返回linxu的应答 list类型
            # logging.debug(str)
        except:
            log = "【ERROR:】SSHConnect 连接数据库失败:\n详细信息:" + traceback.format_exc()
            logging.debug(log)
            self.textBrowser_2.append(log)
        finally:
            ssh.close()

    # ------从Linux下载文件------
    def sftp_download(self, p_oracle_host, p_oracle_port, p_oracle_name, p_oracle_password, p_local, p_oracle_remote):
        sf = paramiko.Transport(p_oracle_host, p_oracle_port) # 生成通道
        sf.connect(username=p_oracle_name, password=p_oracle_password)
        sftp = paramiko.sftp_client.SFTPClient.from_transport(sf)
        try:
            # 检查 p_local 目录是否存在，不存在则创建
            if os.path.exists(p_local):
                pass
            else:
                os.mkdir(p_local)
            try:
                file_exit_flag = '1'
                SFTPAttributes_files = sftp.lstat(p_oracle_remote)
            except:
                log = "【ERROR:】文件不存在，路径:\n详细信息:" + traceback.format_exc()
                logging.debug(log)
                self.textBrowser_2.append(log)
                file_exit_flag = '0'
            # 下载 文件 risk_calculation_result.txt
            try:
                if file_exit_flag == '1':
                    if stat.S_ISDIR(SFTPAttributes_files.st_mode):
                        all_files = get_all_files_in_remote_dir(sftp, p_oracle_remote)
                        for f in all_files:  # 遍历远程目录
                            f_name = f.split('/')[-1]  # 以'/'进行分割，获取列表中最后的元素作为文件名
                            sftp.get(os.path.join(p_oracle_remote, f),
                                     os.path.join(p_local, f_name))  # 下载目录中文件，注意该目录下不允许出现目录。get方法只是用来下载路径下的文件
                    else:
                        f_name = p_oracle_remote.split('/')[-1]
                        sftp.get(p_oracle_remote, os.path.join(p_local, f_name))  # 下载目录中文件，注意该目录下不允许出现目录。get方法只是用来下载路径下的文件
            except:
                log = "【ERROR:】下载数据失败:\n详细信息:" + traceback.format_exc()
                logging.debug(log)
                self.textBrowser_2.append(log)
            # 下载csv文件
            try:
                file_exit_flag = '1'
                G_oracle_remote_csv_file = p_oracle_remote.split(".")[0] + ".csv" # risk_calculation_result.csv
                SFTPAttributes_files = sftp.lstat(G_oracle_remote_csv_file)
            except:
                log = "【ERROR:】文件不存在，路径:\n详细信息:" + traceback.format_exc()
                logging.debug(log)
                self.textBrowser_2.append(log)
                file_exit_flag = '0'
            if file_exit_flag == '1':
                if stat.S_ISDIR(SFTPAttributes_files.st_mode):
                    all_files = get_all_files_in_remote_dir(sftp, G_oracle_remote_csv_file)
                    for f in all_files:  # 遍历远程目录
                        f_name = f.split('/')[-1]  # 以'/'进行分割，获取列表中最后的元素作为文件名
                        sftp.get(os.path.join(G_oracle_remote_csv_file, f), os.path.join(local, f_name))  # 下载目录中文件，注意该目录下不允许出现目录。get方法只是用来下载路径下的文件
                else:
                    f_name = G_oracle_remote_csv_file.split('/')[-1]
                    sftp.get(G_oracle_remote_csv_file, os.path.join(local, f_name))  # 下载目录中文件，注意该目录下不允许出现目录。get方法只是用来下载路径下的文件
        except:
            log = "【ERROR:】Download Exception: 从Linux下载数据失败" + traceback.format_exc()
            logging.debug(log)
            self.textBrowser_2.append(log)
        finally:
            sf.close()

    # 读取csv文件并展示成table_view视图
    def read_csv_file(self):
        # 设置并返回模板
        v_table_model = set_table_model()
        # 加载模板并显示
        self.tableView.setModel(v_table_model)
        row_num, colmun_num = get_csv_row_column_num(file_address=csv_file_address)  # 返回行数、列数
        for i in range(row_num):
            self.tableView.setRowHeight(i, 20) # 遍历设置行高
        for j in range(1, colmun_num):
            self.tableView.setColumnWidth(j, 80)  # 遍历设置行高
        # self.tableView.resizeColumnToContents(0) # 自适应指定列单元格内容调整列宽

    # 获取文件*.txt 并展示在textBrowser
    def show_textOnTextBrowser(self):
        read_file_name = Global_Variables.G_oracle_remote.split('/')[-1]
        file_address = os.path.abspath('.') + '\\download_file\\' + read_file_name
        with open(file_address, "r") as file:
            text_textbrowser = file.read()
            self.textBrowser.append(text_textbrowser)


class Oralce_Set_Dialog_Slots(QDialog, Ui_Dialog):
    def __init__(self, parent=None):
        super(Oralce_Set_Dialog_Slots, self).__init__(parent)
        self.setupUi(self)
        self.show_config()

    # 读取配置文件，并展示
    def show_config(self):
        try:
            # 读取config\config.ini文件
            get_config_ini()
            self.lineEdit.setText(Global_Variables.G_oracle_host)# 设置提示文字
            self.lineEdit_2.setText(str(Global_Variables.G_oracle_port))  # 设置提示文字
            self.lineEdit_3.setText(Global_Variables.G_oracle_name)  # 设置提示文字
            self.lineEdit_4.setText(Global_Variables.G_oracle_password)  # 设置提示文字
            self.lineEdit_5.setText(Global_Variables.G_oracle_remote)  # 设置提示文字
            self.lineEdit_6.setText(Global_Variables.G_db_user_name)  # 设置提示文字
            self.lineEdit_7.setPlaceholderText("请输入数据库密码...")  # 设置提示文字
            # self.lineEdit_6.setAlignment(Qt.AlignCenter) # 居中显示
            self.lineEdit.setFocus()  # 获取焦点
            regExp = QRegExp("(\d+)\.(\d+)\.(\d+)\.(\d+)")  # 正则表达式 只允许输入IP地址
            self.lineEdit.setValidator(QRegExpValidator(regExp))  # 限制输入类型
            regExp = QRegExp("^[0-9]*$")  # 正则表达式 只允许输入数字
            self.lineEdit_2.setValidator(QRegExpValidator(regExp))  # 限制输入类型
            self.lineEdit_2.setMaxLength(5)  # 设置最大输入字符数
            self.lineEdit_7.setEchoMode(2)  # 密码形式输入
        except:
            log = "【ERROR:】Get Config.ini Exception 获取配置文件config.ini失败，请检查配置文件:\n详细信息:" + traceback.format_exc()
            logging.debug(log)
            QMessageBox.warning(self, "警告", log, QMessageBox.Ok)

    # 保存按钮
    @pyqtSlot()
    def on_pushButton_clicked(self):
        information = QMessageBox.information(self, '提示', '是否确认更改配置...', QMessageBox.Yes, QMessageBox.No)
        # 点击确定按钮
        if information == QMessageBox.Yes:
            # 将Oracle配置变量定义为全局变量
            # Oralce服务器IP地址
            G_oracle_host = self.lineEdit.text()
            # Oralce服务器端口
            G_oracle_port = self.lineEdit_2.text()
            # Oralce服务器登录名
            G_oracle_name = self.lineEdit_3.text()
            # Oralce服务器登录密码
            G_oracle_password = self.lineEdit_4.text()
            # Oralce服务器文件地址
            G_oracle_remote = self.lineEdit_5.text()
            # Oralce登陆用户
            G_db_user_name = self.lineEdit_6.text()
            # Oracle登陆密码
            G_db_user_password = self.lineEdit_7.text()
            config_file_adress = os.path.abspath(".") + "\\config\\config.ini"
            if os.path.exists(os.path.abspath(".") + "\\config"):
                pass
            else:
                os.mkdir(os.path.abspath(".") + "\\config")
            try:
                config_file_str = "[oracle_linux_host]\n"               + "host = "         + G_oracle_host + "\n" + \
                                  "\n[oracle_linux_port]\n"             + "port = "         + G_oracle_port + "\n" + \
                                  "\n[oracle_linux_login_name]\n"       + "name = "         + G_oracle_name + "\n" + \
                                  "\n[oracle_linux_login_password]\n"   + "password = "     + G_oracle_password + "\n" + \
                                  "\n[oracle_linux_file_address]\n"     + "address = "      + G_oracle_remote + "\n" + \
                                  "\n[oracle_db_user_name]\n"           + "name = "         + G_db_user_name + "\n" + \
                                  "\n[oracle_db_user_password]\n"       + "password = "     + G_db_user_password + "\n"
                with open(config_file_adress, "w") as config_file:
                    config_file = open(config_file_adress, "w")
                    config_file.write(config_file_str)

                Global_Variables.G_oracle_host = G_oracle_host  # 数据库IP
                Global_Variables.G_oracle_port = int(G_oracle_port)  # 数据库端口
                Global_Variables.G_oracle_name = G_oracle_name  # 数据库所在Linux用户名
                Global_Variables.G_oracle_password = G_oracle_password  # 数据库所在Linux用户密码
                Global_Variables.G_oracle_remote = G_oracle_remote  # 计算文件在Linux中的目录
                Global_Variables.G_db_user_name = G_db_user_name  # 数据库登陆用户
                Global_Variables.G_db_user_password = G_db_user_password  # 数据库登陆密码
                QMessageBox.information(self, '提示', '设置并保存成功', QMessageBox.Ok)
                self.close() # 关闭对话框
            except:
                log = "【ERROR:】Config Exception: 保存失败:\n详细信息:" + traceback.format_exc()
                QMessageBox.critical(self, '警告', log, QMessageBox.Ok)
        else:
            self.close() # 关闭对话框

    # 取消按钮
    @pyqtSlot()
    def on_pushButton_2_clicked(self):
        self.close() # 关闭对话框

# 全局变量
class Global_Variables():
    G_oracle_host      = ' ' # 数据库IP
    G_oracle_port      = 0   # 数据库端口
    G_oracle_name      = ' ' # 数据库所在Linux用户名
    G_oracle_password  = ' ' # 数据库所在Linux用户密码
    G_oracle_remote    = ' ' # 计算文件在Linux中的目录
    G_db_user_name     = ' ' # 数据库登陆用户
    G_db_user_password = ' ' # 数据库登陆密码
    G_customer_theme_name = ' ' # 主题编号

# ------获取远端linux主机上指定目录及其子目录下的所有文件------
def get_all_files_in_remote_dir(sftp, p_oracle_remote):
    remote = p_oracle_remote
    # 保存所有文件的列表
    all_files = list()

    # 去掉路径字符串最后的字符'/'，如果有的话
    if remote[-1] == '/':
        remote = remote[0:-1]

    # 获取当前指定目录下的所有目录及文件，包含属性值
    files = sftp.listdir_attr(remote)
    for x in files:
        # remote目录中每一个文件或目录的完整路径
        filename = remote + '/' + x.filename
        # 如果是目录，则递归处理该目录，这里用到了stat库中的S_ISDIR方法，与linux中的宏的名字完全一致
        if stat.S_ISDIR(x.st_mode):
            all_files.extend(get_all_files_in_remote_dir(sftp, filename))
        else:
            all_files.append(filename)
    return all_files

# 获取csv文件的行数和列数
def get_csv_row_column_num(file_address):
    row_num = 0
    colmun_num = 0
    with open(file_address, "r") as csv_file:
        csv_file_reader = csv.reader(csv_file)
        for row in csv_file_reader:
            row_num += 1 # 获取csv行数
            if len(row) > colmun_num:
                colmun_num = len(row) # 获取最大列数
    return row_num, colmun_num

# 设置table_view模板
def set_table_model():
    global csv_file_address
    csv_file_address = os.path.abspath(".") + "\\download_file\\risk_calculation_result.csv"
    with open(csv_file_address, "r") as csv_file:
        csv_file_reader = csv.reader(csv_file)
        csv_file_data = list(csv_file_reader)  # 将数据转换为list格式 可以通过data[行][列]来获取数据
        row_num, colmun_num = get_csv_row_column_num(file_address=csv_file_address)  # 返回行数、列数
        table_model = QStandardItemModel(row_num, colmun_num)  # 设置tableview模板 并规定设置tableview模板的行数与列数
        for i in range(row_num):
            for j in range(colmun_num):
                try:
                    item_value = csv_file_data[i][j]
                except:
                    item_value = " "
                item = QStandardItem(item_value)
                item.setToolTip(item_value) # 设置tooltip提示
                if Global_Variables.G_customer_theme_name == "Dark":
                    item.setForeground(QBrush(QColor(211, 211, 211)))
                if j == 0:  # 对第一列进行处理，设置不同指标开始结束时 item 中的背景色
                    if item_value[0] == "#":
                        item.setBackground(QBrush(QColor(0, 160, 230))) # RGB设置背景色为灰色
                    list_warnning = re.findall("\*\*\*(警告)\*\*\*", item_value) # 将警告单元格设置为红色。re.findall返回list对象
                    if len(list_warnning):
                        item.setBackground(QBrush(QColor(255, 0, 0))) # RGB设置背景色为红色
                table_model.setItem(i, j, item)  # 设置单元格中的值
    return table_model

# 检查log目录是否存在，如果不存在则创建log目录
def check_log_exist():
    if os.path.exists(os.path.abspath(".") + "\\log"):
        pass
    else:
        os.mkdir(os.path.abspath(".") + "\\log")
    return None



# 读取目录config\config.ini文件
def get_config_ini():
    config = configparser.ConfigParser()
    with open(os.path.abspath(".") + '\\config\\config.ini', 'r') as config_file:
        config.read_file(config_file)
        Global_Variables.G_oracle_host      = config.get("oracle_linux_host", "host")  # 返回string类型 第一个参数为[]"section"， 第二个参数为要获取对应的字段
        Global_Variables.G_oracle_port      = config.getint("oracle_linux_port", "port")  # 返回int类型
        Global_Variables.G_oracle_name      = config.get("oracle_linux_login_name", "name")
        Global_Variables.G_oracle_password  = config.get("oracle_linux_login_password", "password")
        Global_Variables.G_oracle_remote    = config.get("oracle_linux_file_address", "address")
        Global_Variables.G_db_user_name     = config.get("oracle_db_user_name", "name")
        Global_Variables.G_db_user_password = config.get("oracle_db_user_password", "password")

# 读取目录config\custom.ini文件
def get_custom_ini():
    '''
    :return: 定制主题编号
    '''
    config = configparser.ConfigParser()
    with open(os.path.abspath(".") + '\\config\\custom.ini', 'r') as config_file:
        config.read_file(config_file)
        Global_Variables.G_customer_theme_name = config.get("custom_theme", "theme_name")
    return Global_Variables.G_customer_theme_name

def write_custom_ini(p_customer_theme_name):
    Global_Variables.G_customer_theme_name = p_customer_theme_name
    config = configparser.ConfigParser()
    with open(os.path.abspath(".") + '\\config\\custom.ini', 'r') as config_file:
        config.read_file(config_file)
    config.set("custom_theme", "theme_name", p_customer_theme_name)
    with open(os.path.abspath(".") + '\\config\\custom.ini', 'w+') as config_file:
        config.write(config_file)

if __name__ == '__main__':
    local = os.path.abspath('.') + '\\download_file' # 本地文件或目录，与远程一致，当前为windows目录格式，window目录中间需要使用双斜线
    app = QApplication(sys.argv)
    # QStyleFactory 设置窗口风格
    # QApplication.setStyle("WindowsVista") # ['Windows', 'WindowsXP', 'WindowsVista', 'Fusion']
    slot = MainWindow_slots()
    slot.show()
    sys.exit(app.exec_())
